/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
/*
 * Copyright (c) 2010,2011,2012,2013 TELEMATICS LAB, Politecnico di Bari
 *
 * This file is part of LTE-Sim
 *
 * LTE-Sim is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 3 as
 * published by the Free Software Foundation;
 *
 * LTE-Sim is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with LTE-Sim; if not, see <http://www.gnu.org/licenses/>.
 *
 * Author: Francesco Capozzi <f.capozzi@poliba.it>
 */

#include "../channel/LteChannel.h"
#include "../phy/enb-lte-phy.h"
#include "../phy/ue-lte-phy.h"
#include "../core/spectrum/bandwidth-manager.h"
#include "../networkTopology/Cell.h"
#include "../protocolStack/packet/packet-burst.h"
#include "../protocolStack/packet/Packet.h"
#include "../core/eventScheduler/simulator.h"
#include "../flows/application/InfiniteBuffer.h"
#include "../flows/application/VoIP.h"
#include "../flows/application/CBR.h"
#include "../flows/application/TraceBased.h"
#include "../device/IPClassifier/ClassifierParameters.h"
#include "../flows/QoS/QoSParameters.h"
#include "../flows/QoS/QoSForEXP.h"
#include "../flows/QoS/QoSForFLS.h"
#include "../flows/QoS/QoSForM_LWDF.h"
#include "../componentManagers/FrameManager.h"
#include "../utility/seed.h"
#include "../utility/RandomVariable.h"
#include "../utility/UsersDistribution.h"
#include "../utility/IndoorScenarios.h"
#include "../channel/propagation-model/macrocell-urban-area-channel-realization.h"
#include "../channel/propagation-model/femtocell-urban-area-channel-realization.h"
#include "../load-parameters.h"
#include "../device/HeNodeB.h"
#include <iostream>
#include <queue>
#include <fstream>
#include <stdlib.h>
#include <cstring>
#include <math.h>


static void TestThroughputMacroWithFemto (double radius,
        int nbBuildings,
        int nbUE_macro)
{

    srand (time(NULL));

    int nbCell = 19;
    double duration = 10.;
    double flow_duration = duration - 0.2;
    int cluster = 1;
    double bandwidth = 5;


    // CREATE COMPONENT MANAGER
    Simulator *simulator = Simulator::Init();
    FrameManager *frameManager = FrameManager::Init();
    NetworkManager* nm = NetworkManager::Init();
    FlowsManager* flowsManager = FlowsManager::Init ();


    // CREATE MACRO CELLS with eNB
    for (int i = 0; i < nbCell; i++)
    {
        CartesianCoordinates center = GetCartesianCoordinatesForCell(i, radius);
        Cell *c = new Cell (i, radius/1000., 0.035, center.GetCoordinateX (), center.GetCoordinateY ());
        nm->GetCellContainer ()->push_back (c);

        std::cout << "CELL " << c->GetIdCell ()
                  <<" position " << c->GetCellCenterPosition ()->GetCoordinateX ()
                  << " " << c->GetCellCenterPosition ()->GetCoordinateY () << std::endl;

        ENodeB* enb = new ENodeB (i, c);
        LteChannel* ch_dl = new LteChannel ();
        ch_dl->SetChannelId (i);
        LteChannel* ch_ul = new LteChannel ();
        ch_ul->SetChannelId (i);
        enb->GetPhy ()->SetDlChannel (ch_dl);
        enb->GetPhy ()->SetUlChannel (ch_ul);
        enb->SetDLScheduler (ENodeB::DLScheduler_TYPE_PROPORTIONAL_FAIR);
        enb->GetPhy ()->SetBandwidthManager (new BandwidthManager (5, 5, 0, 0));
        nm->GetENodeBContainer ()->push_back (enb);

        std::cout << "eNB " << enb->GetIDNetworkNode()
                  << " cell " << enb->GetCell ()->GetIdCell ()
                  <<" position " << enb->GetMobilityModel ()->GetAbsolutePosition ()->GetCoordinateX ()
                  << " " << enb->GetMobilityModel ()->GetAbsolutePosition ()->GetCoordinateY ()
                  << " channels " << enb->GetPhy ()->GetDlChannel ()->GetChannelId ()
                  << " " << enb->GetPhy ()->GetUlChannel ()->GetChannelId ()  << std::endl;
    }

    // CREATE FEMTOCELLS with HeNB
    int idFemto = 101;
    int idBuilding = 1101;
    int buildType = 0; // TYPE_3GPP_5x5_grid
    int nbFloors = 1;
    int apartmentSide = 10;
    int femtoCellsInBuilding = 25;
    int nbFemtoCells = nbBuildings * femtoCellsInBuilding;

    for (int i = 0; i < nbBuildings; i++)
    {
        double buildingCenter_X = (double) sin (i * (M_PI/2)) * (radius * 0.5);
        double buildingCenter_Y = (double) cos (i * (M_PI/2)) * (radius * 0.5);

        nm->CreateBuildingForFemtocells(idBuilding, buildType, apartmentSide, nbFloors, buildingCenter_X, buildingCenter_Y, idFemto, femtoCellsInBuilding);

        std::cout << "Building " << idBuilding
                  <<" position " << buildingCenter_X
                  << " " << buildingCenter_Y
                  << " with " << femtoCellsInBuilding << " femtocells" << std::endl;

        //create HeNB
        for (int j = 0; j < femtoCellsInBuilding; j++)
        {
            HeNodeB* enb = new HeNodeB (idFemto + j, nm->GetFemtoCellContainer ()->at (j + (i*femtoCellsInBuilding)));
            LteChannel* ch_dl = new LteChannel ();
            ch_dl->SetChannelId (idFemto + j);
            LteChannel* ch_ul = new LteChannel ();
            ch_ul->SetChannelId (idFemto + j);
            enb->GetPhy ()->SetDlChannel (ch_dl);
            enb->GetPhy ()->SetUlChannel (ch_ul);
            enb->SetDLScheduler (ENodeB::DLScheduler_TYPE_PROPORTIONAL_FAIR);
            enb->GetPhy ()->SetBandwidthManager (new BandwidthManager (5, 5, 50, 50));
            nm->GetHomeENodeBContainer()->push_back (enb);

            std::cout << "HeNB " << enb->GetIDNetworkNode()
                      << " cell " << enb->GetCell ()->GetIdCell ()
                      <<" position " << enb->GetMobilityModel ()->GetAbsolutePosition ()->GetCoordinateX ()
                      << " " << enb->GetMobilityModel ()->GetAbsolutePosition ()->GetCoordinateY ()
                      << " channels " << enb->GetPhy ()->GetDlChannel ()->GetChannelId ()
                      << " " << enb->GetPhy ()->GetUlChannel ()->GetChannelId ()  << std::endl;

        }

        idFemto = idFemto + femtoCellsInBuilding;
        idBuilding++;
    }









    int idApp = 0;
    int dstPort = 0;
    int srcPort = 0;
    double startTime = 0.1;

    // CREATE UE into the MACRO CELL
    int idUE = 3001;
    int j = 0;
    //for (int j = 0; j < nbCell; j++)
    //  {
    for (int i = 0; i < nbUE_macro; i++)
    {
        double posX = (double)rand()/RAND_MAX;
        posX = 0.90 * (((2*radius)*posX) - radius);
        double posY = (double)rand()/RAND_MAX;
        posY = 0.90 * (((2*radius)*posY) - radius);
        double speed = 3;
        double speedDirection = (double)(rand() %360) * ((2*3.14)/360);

        UserEquipment* ue = new UserEquipment (idUE,
                                               posX, posY, speed, speedDirection,
                                               nm->GetCellByID (j),
                                               nm->GetENodeBByCellID (j),
                                               1, //handover false!
                                               Mobility::CONSTANT_POSITION);
        ue->SetIndoorFlag(false);

        nm->SelectTargetNode (ue);
        ENodeB* target = (ENodeB*) ue->GetTargetNode ();
        if (target->GetNodeType () == NetworkNode::TYPE_HOME_BASE_STATION)
        {
            ue->SetIndoorFlag (true);
        }

        ue->GetPhy ()->SetDlChannel (target->GetPhy ()->GetDlChannel ());
        ue->GetPhy ()->SetUlChannel (target->GetPhy ()->GetUlChannel ());

        FullbandCqiManager *cqiManager = new FullbandCqiManager ();
        cqiManager->SetCqiReportingMode (CqiManager::PERIODIC);
        cqiManager->SetReportingInterval (1);
        cqiManager->SetDevice (ue);
        ue->SetCqiManager (cqiManager);

        target->RegisterUserEquipment (ue);
        MacroCellUrbanAreaChannelRealization* c_dl = new MacroCellUrbanAreaChannelRealization (target, ue);
        target->GetPhy ()->GetDlChannel ()->GetPropagationLossModel ()->AddChannelRealization (c_dl);
        MacroCellUrbanAreaChannelRealization* c_ul = new MacroCellUrbanAreaChannelRealization (ue, target);
        target->GetPhy ()->GetUlChannel ()->GetPropagationLossModel ()->AddChannelRealization (c_ul);

        nm->GetUserEquipmentContainer ()->push_back (ue);


        std::cout << "UE " << idUE << " position " << posX << " " << posY
                  << " cell " <<  ue->GetCell ()->GetIdCell ()
                  << " enb " << ue->GetTargetNode ()->GetIDNetworkNode () << std::endl;

        //Create an Application
        QoSParameters *qos = new QoSParameters ();
        Application* be = flowsManager->CreateApplication (idApp,
                          target, ue,
                          srcPort, dstPort, TransportProtocol::TRANSPORT_PROTOCOL_TYPE_UDP ,
                          Application::APPLICATION_TYPE_INFINITE_BUFFER,
                          qos,
                          startTime, startTime + flow_duration);

        ue->GetPhy ()->GetDlChannel ()->AddDevice (ue);
        idUE++;
        idApp++;
        dstPort++;
    }



    simulator->SetStop(duration);
    simulator->Run ();



    for (std::vector<UserEquipment*>::iterator it = nm->GetUserEquipmentContainer ()->begin (); it != nm->GetUserEquipmentContainer ()->end (); it++)
    {
        UserEquipment* ue = (*it);
        if (ue->GetTargetNode ()->GetNodeType () == NetworkNode::TYPE_ENODEB)
        {
            std::cout << "macro " << ue->GetMobilityModel ()->GetAbsolutePosition ()->GetCoordinateX ()
                      << " " << ue->GetMobilityModel ()->GetAbsolutePosition ()->GetCoordinateY ()  << std::endl;
        }
        else if (ue->GetTargetNode ()->GetNodeType () == NetworkNode::TYPE_HOME_BASE_STATION)
        {
            std::cout << "femto " << ue->GetMobilityModel ()->GetAbsolutePosition ()->GetCoordinateX ()
                      << " " << ue->GetMobilityModel ()->GetAbsolutePosition ()->GetCoordinateY ()  << std::endl;
        }
        else
        {
            std::cout << "not available" << std::endl;
        }

    }


}
